/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
 */

package com.huawei.api.task;

import com.huawei.api.bean.CloudDefect;
import com.huawei.api.bean.CmdExecResult;
import com.huawei.api.bean.CmdParameter;
import com.huawei.api.bean.CommandInfo;
import com.huawei.api.bean.ThreadPool;
import com.huawei.api.bean.UseType;
import com.huawei.api.cmd.ToolRunner;
import com.huawei.api.exception.BaseCustomerException;
import com.huawei.api.handler.ReportHandler;
import com.huawei.api.utils.PathUtil;
import com.huawei.api.utils.StringConvertUtil;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.exec.CommandLine;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;

/**
 * 工具命令执行任务
 *
 * @since 2023/10/28
 */
@Data
@Slf4j
public class FormatCheckTask implements Task {
    private static final Logger LOGGER = LoggerFactory.getLogger(FormatCheckTask.class);

    private final List<CmdParameter> cmdParameters;

    private final String outputFilePath;

    private List<String> errorMsgList = new ArrayList<>();

    private final List<CloudDefect> defectResult = new ArrayList<>();

    public FormatCheckTask(List<CmdParameter> cmdParameters, String outputFilePath) {
        this.cmdParameters = cmdParameters;
        this.outputFilePath = outputFilePath;
    }

    @Override
    public void execute() throws BaseCustomerException {
        //封装cmd命令
        List<CommandInfo> commandInfos = this.getCommandInfos();
        if (commandInfos.size() == 0) {
            return;
        }
        // 异步执行任务
        int cmdSize = commandInfos.size();
        // 执行异步任务
        List<Callable<ToolRunner>> callableList = new ArrayList<>(cmdSize);
        for (int i = 0; i < cmdSize; i++) {
            final CommandInfo commandInfo = commandInfos.get(i);
            final int step = i + 1;
            callableList.add(() -> runAnalyzer(commandInfo, step, cmdSize));
        }
        int threadPoolSize = Math.min(cmdSize, 5);
        // 执行
        ThreadPool threadPool = new ThreadPool(threadPoolSize);
        threadPool.call(callableList, toolRunner -> errorMsgList.addAll(toolRunner.getErrorMsg()));
    }

    /**
     * 获取所有的命令行
     *
     * @return 命令行数据
     */
    private List<CommandInfo> getCommandInfos() throws BaseCustomerException {
        // 如果只有一个命令行,直接输出到制定的输出文件中;如果是多个命令行，输出到各自子文件中再汇总到指定的输出文件
        List<CommandInfo> commandInfos = getSingleCmd(cmdParameters.get(0));
        return commandInfos;
    }

    /**
     * 获取执行器
     *
     * @param commandInfo 命令行信息
     * @param step 步骤
     * @param cmdSize 总命令
     * @return ToolRunner 执行工具
     * @throws BaseCustomerException 异常
     */
    private ToolRunner runAnalyzer(CommandInfo commandInfo, int step, int cmdSize) throws BaseCustomerException {
        ToolRunner runner = new ToolRunner(commandInfo, 3600 * 5, true, step, cmdSize);
        // 执行命令
        CmdExecResult result = runner.run();
        if (null == result || result.getActualResultCode() != 0) {
            runner.getErrorMsg().add("step " + step + " run [" + commandInfo.getCommandLine().toString() + "] failed!");
            return runner;
        }
        // 解析结果
        // 做区分
        ReportHandler reportHandler = new ReportHandler(commandInfo.getReportFilePath());
        reportHandler.handle();

        List<CloudDefect> defects = reportHandler.getDefects();
        // 判断是否存在扩展内容(目前只有ts规则)
        checkExtendInfo(commandInfo, defects);
        // 合并报告数据  此处合并报告
        defectResult.addAll(defects);

        runner.setStep(step);
        runner.setTotal(cmdSize);
        return runner;
    }

    private void checkExtendInfo(CommandInfo commandInfo, List<CloudDefect> defects) {
        // 判空
        if (ObjectUtils.isNotEmpty(commandInfo) && ObjectUtils.isNotEmpty(commandInfo.getCommandLine())
            && commandInfo.getCommandLine().getExecutable().contains("apiTsLintStart")) {
            for (CloudDefect defect : defects) {
                try {
                    defect.setExtraDegradation(JSON.toJSONString(defect.getExtendInfo()));
                } catch (JSONException exception) {
                    log.error("check extend info error, path is {}, defectType is {}, Line num is {}",
                        defect.getBuggyFilePath(), defect.getDefectType(), defect.getMainBuggyLine());
                }
            }
        }
    }

    /**
     * 单个cmd参数构建cmd命令
     *
     * @param cmdParameter cmd命令参数
     * @return List<CommandInfo> cmd命令列表
     */
    private List<CommandInfo> getSingleCmd(CmdParameter cmdParameter) throws BaseCustomerException {
        String cmd;
        log.info("type is: {}", cmdParameter.getType());
        // 如果是本地执行模式只有windows环境，如果是集成模式，区分windows环境和linux环境
        if (cmdParameter.getType().equals(UseType.LOCAL.type)) {
            cmd = getLocalCmd(cmdParameter);
        } else if (SystemUtils.IS_OS_WINDOWS) {
            cmd = getWindowsCmd(cmdParameter);
        } else {
            cmd = getLinuxCmd(cmdParameter);
        }
        log.info("the string type cmd is: {}", StringConvertUtil.convert(cmd, "="));
        CommandLine parse = CommandLine.parse(cmd);
        return Arrays.asList(new CommandInfo(parse, outputFilePath));
    }

    private String getLocalCmd(CmdParameter cmdParameter) throws BaseCustomerException {
        return getWindowsCmd(cmdParameter);
    }

    private String getWindowsCmd(CmdParameter cmdParameter) throws BaseCustomerException {
        String cmd = cmdParameter.getLocalToolPath() + File.separator + cmdParameter.getCodeArtsToolCmd();
        String fullPathCmd = PathUtil.getAbsolutePath(cmdParameter.getPluginPath(), cmd);
        // 1-md文件路径，写入本地txt文件，把对应绝对路径传过去(待定)，2-扫描的源码绝对路径，3-分支，4-规则@符号隔开，5-报表路径
        String paramFilePath = cmdParameter.getParamFilePath(); // 1-md文件路径
        String source = cmdParameter.getSource(); // 2.扫描的源码绝对路径
        String branch = cmdParameter.getBranch(); // 3.分支
        String output = cmdParameter.getOutput();
        String toolPath = cmdParameter.getToolPath();
        return String.format(fullPathCmd, paramFilePath, source, branch, cmdParameter.getCheckerPath(), output, toolPath);
    }

    private String getLinuxCmd(CmdParameter cmdParameter) throws BaseCustomerException {
        String cmd = cmdParameter.getLocalToolPath() + File.separator + cmdParameter.getCodeArtsToolLinuxCmd();
        LOGGER.info("<<< FormatCheckTask getLinuxCmd cmd1 is ={}", cmd);
        String fullPathCmd = PathUtil.getAbsolutePath(cmdParameter.getToolPath(), cmd);
        LOGGER.info("<<< FormatCheckTask getLinuxCmd cmd2 is ={}", fullPathCmd);
        // 1-md文件路径，写入本地txt文件，把对应绝对路径传过去(待定)，2-扫描的源码绝对路径，3-分支，4-规则@符号隔开，5-报表路径
        String paramFilePath = cmdParameter.getParamFilePath(); // 1-md文件路径
        String source = cmdParameter.getSource(); // 2.扫描的源码绝对路径
        String branch = cmdParameter.getBranch(); // 3.分支
        String output = cmdParameter.getOutput();
        String toolPath = cmdParameter.getToolPath();
        return String.format(fullPathCmd, paramFilePath, source, branch, cmdParameter.getCheckerPath(), output, toolPath);
    }
}
